package com.smart.community.property.aspect;

import com.smart.community.commons.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;

/**
 * 物业公司模块操作日志切面
 * 
 * 严格按照《Feign接口开发规范.md》要求实现：
 * - Feign接口调用时不依赖用户上下文
 * - 避免在Feign接口调用时尝试获取用户ID
 * - 区分业务接口和Feign接口的日志记录
 * 
 * @author Wu.Liang
 * @date 2025-01-29
 * @version 2.0.0
 */
@Slf4j
@Aspect
@Component
public class OperationLogAspect {

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 方法执行前记录日志
     */
    @Before("execution(* com.smart.community.property.controller.*.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        try {
            String methodName = joinPoint.getSignature().getName();
            String className = joinPoint.getTarget().getClass().getSimpleName();
            String args = Arrays.toString(joinPoint.getArgs());
            String timestamp = LocalDateTime.now().format(FORMATTER);
            
            HttpServletRequest request = getRequest();
            String ip = getClientIp(request);
            String userAgent = request != null ? request.getHeader("User-Agent") : "未知";
            
            // 判断是否为Feign接口调用
            boolean isFeignCall = isFeignInterfaceCall(request);
            
            String userId;
            String username;
            
            if (isFeignCall) {
                // Feign接口调用时不尝试获取用户ID，避免WARN警告
                userId = "Feign调用";
                username = "Feign调用";
            } else {
                // 业务接口调用时正常获取用户ID
                Long currentUserId = SecurityUtils.getCurrentUserId();
                userId = currentUserId != null ? currentUserId.toString() : "未知";
                username = "未知"; // 暂时设为未知，后续可以扩展
            }

            log.info("=== 操作日志开始 ===");
            log.info("时间: {}", timestamp);
            log.info("类名: {}", className);
            log.info("方法名: {}", methodName);
            log.info("请求参数: {}", args);
            log.info("请求方法: {}", request != null ? request.getMethod() : "未知");
            log.info("请求URL: {}", request != null ? request.getRequestURL() : "未知");
            log.info("IP地址: {}", ip);
            log.info("用户代理: {}", userAgent);
            log.info("用户ID: {}", userId);
            log.info("用户名: {}", username);
        } catch (Exception e) {
            log.error("记录操作日志失败", e);
        }
    }

    /**
     * 判断是否为Feign接口调用
     * 
     * 根据《Feign接口开发规范.md》，Feign接口使用/feign前缀
     * 
     * @param request HTTP请求
     * @return 是否为Feign接口调用
     */
    private boolean isFeignInterfaceCall(HttpServletRequest request) {
        if (request == null) {
            return false;
        }
        
        String requestURI = request.getRequestURI();
        String userAgent = request.getHeader("User-Agent");
        
        // 1. 检查请求路径是否包含/feign前缀
        if (requestURI != null && requestURI.startsWith("/feign/")) {
            return true;
        }
        
        // 2. 检查User-Agent是否为Java客户端（Feign调用特征）
        if (userAgent != null && userAgent.startsWith("Java/")) {
            return true;
        }
        
        // 3. 检查IP地址是否为内部调用（192.168.x.x）
        String clientIp = getClientIp(request);
        if (clientIp != null && clientIp.startsWith("192.168.")) {
            return true;
        }
        
        return false;
    }

    /**
     * 方法执行成功后记录日志
     */
    @AfterReturning(pointcut = "execution(* com.smart.community.property.controller.*.*(..))", returning = "result")
    public void afterMethodReturning(JoinPoint joinPoint, Object result) {
        try {
            String methodName = joinPoint.getSignature().getName();
            String className = joinPoint.getTarget().getClass().getSimpleName();
            String timestamp = LocalDateTime.now().format(FORMATTER);
            
            log.info("执行结果: {}", result);
            log.info("执行状态: 成功");
            log.info("=== 操作日志结束 ===");
        } catch (Exception e) {
            log.error("记录操作日志失败", e);
        }
    }

    /**
     * 方法执行异常时记录日志
     */
    @AfterThrowing(pointcut = "execution(* com.smart.community.property.controller.*.*(..))", throwing = "ex")
    public void afterMethodThrowing(JoinPoint joinPoint, Throwable ex) {
        try {
            String methodName = joinPoint.getSignature().getName();
            String className = joinPoint.getTarget().getClass().getSimpleName();
            String timestamp = LocalDateTime.now().format(FORMATTER);
            
            log.error("=== 操作日志异常 ===");
            log.error("时间: {}", timestamp);
            log.error("类名: {}", className);
            log.error("方法名: {}", methodName);
            log.error("异常信息: {}", ex.getMessage());
            log.error("执行状态: 失败");
            log.error("异常堆栈: ", ex);
        } catch (Exception e) {
            log.error("记录操作日志失败", e);
        }
    }

    /**
     * 获取HTTP请求对象
     */
    private HttpServletRequest getRequest() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            return attributes != null ? attributes.getRequest() : null;
        } catch (Exception e) {
            log.warn("获取HTTP请求对象失败", e);
            return null;
        }
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        if (request == null) {
            return "未知";
        }
        
        try {
            // 尝试从各种请求头获取真实IP
            String[] headerNames = {
                "X-Forwarded-For",
                "X-Real-IP", 
                "Proxy-Client-IP",
                "WL-Proxy-Client-IP",
                "HTTP_CLIENT_IP",
                "HTTP_X_FORWARDED_FOR"
            };
            
            for (String headerName : headerNames) {
                String ip = request.getHeader(headerName);
                if (ip != null && !ip.trim().isEmpty() && !"unknown".equalsIgnoreCase(ip.trim())) {
                    // 如果是多个IP，取第一个
                    if (ip.contains(",")) {
                        ip = ip.split(",")[0].trim();
                    }
                    return ip;
                }
            }
            
            // 如果请求头中没有，使用RemoteAddr
            return request.getRemoteAddr();
        } catch (Exception e) {
            log.warn("获取客户端IP地址失败", e);
            return "未知";
        }
    }
}
